﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.Xml;
using PloobsEngine.Physics;
using PloobsEngine.Utils;
using PloobsEngine.Modelo;
using PloobsEngine.Material;
using PloobsEngine.Publishers;
using PloobsEngine.IA;
using PloobsEngine.Behaviors;
using PloobsEngine.Cameras;
using PloobsEngine.Light;
using PloobsEngine.MessageSystem;
using PloobsEngine.Events;

namespace PloobsEngine.SceneControl
{
    

    public class SimpleObject : IObject
    {
        private bool castShadow = true;
        private IMaterial _material;
        private IPhysicObject _physicObject;
        private IModelo _model;
        private bool _hasMoved = true;        
        private IBehaviors _behaviors;
        private int id;
        private String _name;
        private List<IObjectAtachtment> _atachs = new List<IObjectAtachtment>();
        private IAgent _agente = null;
        private Vector3 lastPosition = new Vector3(float.NaN);
        

        /// <summary>
        /// Serialization 
        /// </summary>
        public SimpleObject()
        {
        }

        public SimpleObject(IMaterial material , IPhysicObject physicObject, IModelo modelo)
        {
            this._material = material;
            this._physicObject = physicObject;
            this._model = modelo;
            this._name = null;
            this._behaviors = null;
            lastPosition = physicObject.Position;
            init();
        }

        private void init()
        {
            _obs = new ConcreteObserver(this);
        }
        public virtual Matrix getWorldMatrix()
        {            
            return _physicObject.WorldMatrix;
        }
       
        private IObserver _obs;

        #region IObject Members

      
        public IBehaviors Behavior
        {
            get
            {
                return _behaviors;
            }
            set
            {
                this._behaviors = value;
            }
        }

        public bool hasChanged
        {
            get { return _hasMoved; }
        }

        public virtual void Drawn(Microsoft.Xna.Framework.GameTime gt, ICamera cam, IList<ILight> lights,IRenderHelper render)
        {
            if (Material != null)
                Material.Drawn(gt, this, cam, lights, render);
        }
        
        public virtual void UpdateObject(Microsoft.Xna.Framework.GameTime gt, ICamera cam, IList<ILight> luzes)
        {            
            if (_material != null)
                Material.Update(gt, this, luzes);

            foreach (IObjectAtachtment item in _atachs)
            {
                item.Update(this,gt);
            }

            if (OnUpdate != null)
                OnUpdate(this);

            if (_agente != null)
                _agente.Update(gt);

            if (Vector3.DistanceSquared(this.Position,lastPosition) > 0.1f)
            {
                _hasMoved = true;
                if (OnHasMoved != null)
                    OnHasMoved(this);
            }
            else
            {
                _hasMoved = false;
            }

            lastPosition = this.Position;


        }

        #endregion
      
        #region IRecieveMessageEntity Members

        public virtual bool HandleThisMessageType(SenderType type)
        {
            return true;
        }

        public virtual void HandleMessage(Message mes)
        {
            if(em.HandleThisType(mes.SenderType))
                em.HandleEvents(mes);

            if (OnRecieveMessage != null)
                OnRecieveMessage(this, mes);

        }

        #endregion

        #region IObserver Members

        public void AddSubject(ISubject sub)
        {
            _obs.AddSubject(sub);
        }

        public void RemoveSubject(ISubject sub)
        {
            _obs.RemoveSubject(sub);
        }

        /// <summary>
        /// Implementacao do Update, recebera a notificacao do Subject e a tratara
        /// </summary>
        /// <param name="obj"></param>
        public virtual void Recieve(object obj)
        {            
        }

        public void DetachFromAllSubjects()
        {
            _obs.DetachFromAllSubjects();
        }

        #endregion

        #region IEntity Members

        public int getId()
        {
            return id;
        }

        public void setId(int id)
        {
            this.id = id;
        }

        #endregion

        #region IObject Members


        public virtual IPhysicObject PhysicObject
        {
            get
            {
                return _physicObject;
            }
            set
            {
                _physicObject = value;
            }
        }

        public virtual IModelo Modelo
        {
            get
            {
                return _model;
            }
            set
            {
                _model = value;
            }
        }

        public virtual IMaterial Material
        {
            get
            {
                return _material;
            }
            set
            {
                _material = value;
            }
        }

        public String Name
        {
            get {
                return _name;
            }
            set {
                _name = value;
            }
        }

        #endregion

        #region IObject Members


        public virtual Microsoft.Xna.Framework.Vector3 Position
        {
            get
            {
                return _physicObject.Position;
            }
            set
            {
                _physicObject.Position = value;
            }
        }

        public virtual Matrix Rotation
        {
            get
            {
                return _physicObject.Rotation;
            }
            set
            {
                _physicObject.Rotation = value;
            }
        }

        public virtual Microsoft.Xna.Framework.Vector3 Scale
        {
            get
            {
                return _physicObject.Scale;
            }
            set
            {
                _physicObject.Scale = value;
            }
        }

        #endregion

        #region IObject Members
        private EventManager em = new EventManager();

        public EventManager EventsManager
        {
            get { return em; }
        }

        #endregion

        #region IObject Members


        public void AddAtachment(IObjectAtachtment obj)
        {
            _atachs.Add(obj);
        }

        public void RemoveAtachment(IObjectAtachtment obj)
        {
            _atachs.Remove(obj);
        }

        #endregion

        #region IObject Members


        public IAgent Agente
        {
            get
            {
                return _agente;
            }
            set
            {
                this._agente = value;
            }
        }

        #endregion

        public void Serialize(System.Xml.XmlTextWriter textWriter)
        {            
                SerializerHelper.SerializeBaseType<string>(Name, "Name", textWriter);            
                SerializerHelper.SerializeVector3(Position, "Position", textWriter);
                SerializerHelper.SerializeMatrix(Rotation, "Rotation", textWriter);

                SerializerHelper.SerializeNewElement("Model", textWriter);
                SerializerHelper.SerializeAttributeBaseType<string>(Modelo.GetType().AssemblyQualifiedName, "Type", textWriter);
                Modelo.Serialize(textWriter);
                SerializerHelper.SerializeEndElement(textWriter);

                SerializerHelper.SerializeNewElement("PhysicObject", textWriter);
                SerializerHelper.SerializeAttributeBaseType<string>(PhysicObject.GetType().AssemblyQualifiedName, "Type", textWriter);
                PhysicObject.Serialize(textWriter);
                SerializerHelper.SerializeEndElement(textWriter);

                SerializerHelper.SerializeNewElement("Material", textWriter);
                if (Material != null)
                {
                    SerializerHelper.SerializeAttributeBaseType<String>(Material.GetType().AssemblyQualifiedName, "Type", textWriter);
                    Material.Serialize(textWriter);
                }
                else
                {
                    SerializerHelper.SerializeAttributeBaseType<String>(null, "Type", textWriter);
                }

                SerializerHelper.SerializeEndElement(textWriter);

        }



        public void DeSerialize(System.Xml.XmlNode node,EngineStuff engine, IWorld world)
        {
            
            Vector3 pos =  SerializerHelper.DeserializeVector3("Position", node);
            Matrix rot =  SerializerHelper.DeSerializeMatrix("Rotation", node);
            this.Name = SerializerHelper.DeserializeBaseType<string>("Name", node);

            XmlElement mod = node["Model"];
            string modtype = SerializerHelper.DeserializeAttributeBaseType<string>("Type", mod);
            Type tt = Type.GetType(modtype);
            IModelo modelo = Activator.CreateInstance(tt) as IModelo;
            modelo.DeSerialize(mod,engine);
            this.Modelo = modelo;

            XmlElement phiNode = node["PhysicObject"];
            String collisionType = SerializerHelper.DeserializeAttributeBaseType<string>("Type", phiNode);
            Type phitype = Type.GetType(collisionType);
            IPhysicObject obj = Activator.CreateInstance(phitype) as IPhysicObject;
            obj.DeSerialize(phiNode, engine, modelo, pos, rot);
            this.PhysicObject = obj;


            XmlElement materialNode = node["Material"];
            String materialType =  SerializerHelper.DeserializeAttributeBaseType<string>("Type", materialNode);
            if (!String.IsNullOrEmpty(materialType))
            {
                Type mt = Type.GetType(materialType);
                IMaterial mat = Activator.CreateInstance(mt) as IMaterial;
                mat.DeSerialize(materialNode,engine,modelo);
                this.Material = mat;

            }
            else
            {
                this.Material = null;
            }

            init();

        }


        #region IObject Members

        private bool remove = false;
        public bool ToBeRemoved
        {
            get
            {
                return remove;
            }
            set
            {
                this.remove = value;
                if (value == true)
                {
                    if (OnBeingRemoved != null)
                        OnBeingRemoved(this);
                }
            }
        }

        #endregion

        #region IObject Members


        public bool CastShadow
        {
            get
            {
                return castShadow;
            }
            set
            {
                this.castShadow = value;
            }
        }

        #endregion

        #region IObject Members


        public void PreDrawn(IWorld mundo,GameTime gt, ICamera cam, IList<ILight> lights, IRenderHelper render)
        {
            if (Material!= null)
                Material.PreDrawnPhase(mundo,gt, this, cam, lights, render);
        }

        public void PosDrawn(GameTime gt, ICamera cam, IList<ILight> lights, IRenderHelper render)
        {
            if (Material != null)
                Material.PosDrawnPhase(gt, this, cam, lights, render);
        }

        
        #endregion

        #region IObject Members


        public float CameraDistance
        {
            get;
            set;
        }

        #endregion

        #region IObject Members


        public event BeingRemoved OnBeingRemoved = null;

        #endregion

        #region IObject Members


        public event OnUpdate OnUpdate = null;

        #endregion

        #region IObject Members

        public event OnRecieveMessage OnRecieveMessage = null;

        #endregion

        #region IObject Members


        public event OnHasMoved OnHasMoved = null;

        #endregion
    }
}
